home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Libraries / TurboTCP 1.0.1 / TurboTCP.source / CTCPResolverCall.cp < prev    next >
Text File  |  1993-12-10  |  17KB  |  797 lines

  1. /*
  2. ** CTCPResolverCall.cp
  3. **
  4. **    TurboTCP support library
  5. **    TCP resolver call class
  6. **
  7. **    Copyright © 1993, FrostByte Design / Eric Scouten
  8. **    Some portions adapted from file “dnr.c” Copyright © 1988 by Apple Computer. All rights reserved.
  9. **
  10. */
  11.  
  12.  
  13. #include "CTCPResolverCall.h"
  14.  
  15. #ifndef TurboTCPHeaders
  16.     #include <Folders.h>
  17.     #include <GestaltEqu.h>
  18.     #include <Traps.h>
  19.     #include <TCLUtilities.h>
  20.     #include <OSChecks.h>
  21.     #include "TurboTCP.const.h"
  22. #endif
  23. #include "CTCPDriver.h"
  24.  
  25.  
  26. // procedure code numbers for DNR code segment
  27.  
  28. #define OPENRESOLVER    1L
  29. #define CLOSERESOLVER    2L
  30. #define STRTOADDR        3L
  31. #define ADDRTOSTR        4L
  32. #define ENUMCACHE        5L
  33. #define ADDRTONAME    6L
  34. #define HINFO            7L
  35. #define MXINFO            8L
  36.  
  37.  
  38. // declaration & initialization of class variables
  39.  
  40. Handle        CTCPResolverCall::macDNRcode = NULL;
  41. DNRProcPtr    CTCPResolverCall::macDNRentry = NULL;
  42.  
  43.  
  44. //    —— initialize/destroy resolver ——
  45.  
  46. /*______________________________________________________________________
  47. **
  48. ** ITCPResolverCall
  49. **
  50. **    Initialize the resolver object. Must keep the object locked, since pointers are used
  51. **    frequently.
  52. **
  53. */
  54.  
  55. void CTCPResolverCall::ITCPResolverCall (void)
  56.  
  57. {
  58.  
  59.     // clear variables
  60.     
  61.     inUse = FALSE;
  62.     disposeOnCompletion = FALSE;
  63.     pendingNotify = notifNone;
  64.     qEntry.qSelfLink = this;
  65.     qEntry.qType = resolverCall;
  66.  
  67.     
  68.     // initialize the collaborator object
  69.     
  70.     CCollaborator::ICollaborator();
  71.  
  72.  
  73.     // prevent this from moving, since we use pointers
  74.  
  75.     MoveHHi((Handle) this);
  76.     this->Lock(TRUE);
  77.  
  78. }
  79.  
  80.  
  81. /*______________________________________________________________________
  82. **
  83. ** Dispose
  84. **
  85. **    Get rid of the resolver object. If a resolver operation is in progress, will dispose of
  86. **    itself when the current resolver operation is completed.
  87. **
  88. */
  89.  
  90. void CTCPResolverCall::Dispose (void)
  91.  
  92. {
  93.     if (inUse)
  94.         disposeOnCompletion = TRUE;
  95.     else
  96.         CCollaborator::Dispose();
  97. }
  98.  
  99.  
  100. //    —— initiate resolver calls ——
  101.  
  102. /*______________________________________________________________________
  103. **
  104. ** DoStrToAddr
  105. **
  106. **    Get the IP address of a host named by a DNS or dotted decimal string. Completion is
  107. **    handled by HandleStrToAddr.
  108. **
  109. **        theHostName (char *):    whos IP address do we need?
  110. **
  111. */
  112.  
  113. void CTCPResolverCall::DoStrToAddr (char *theHostName)
  114.  
  115. {
  116.     OSErr theResult;
  117.     
  118.     if (inUse)
  119.         FailOSErr(resolverInUse);
  120.     if (!gTCPDriver->CheckResolver())
  121.         FailOSErr(noResolverErr);
  122.     
  123.     inUse = TRUE;
  124.     gTCPDriver->RegisterActiveResolver(this);
  125.     BlockMove(theHostName, &hostName, 255);
  126.     theResult = (*macDNRentry)(STRTOADDR, (char *) &hostName, &theHostInfo,
  127.                             &PostponeStrToAddr, (char *) this);
  128.  
  129.  
  130.     // if call was completed immediately (with error or not), process it immediately
  131.     
  132.     if (theResult != cacheFault) {
  133.         inUse = FALSE;
  134.         gTCPDriver->RemoveActiveResolver(this);
  135.         HandleStrToAddr();
  136.     }
  137. }
  138.  
  139.  
  140. /*______________________________________________________________________
  141. **
  142. ** DoAddrToStr
  143. **
  144. **    Convert an IP address to dotted decimal notation. This call can be used while the resolver
  145. **    object is otherwise in use, and is completed immediately.
  146. **
  147. **        theIPaddr (ip_addr):    the address to convert
  148. **        theString (char *):    where to send the completed string (min. 16 chars)
  149. **
  150. */
  151.  
  152. void CTCPResolverCall::DoAddrToStr (ip_addr theIPaddr, char *theString)
  153.  
  154. {
  155.     OSErr theResult;
  156.     
  157.     if (!gTCPDriver->CheckResolver())
  158.         FailOSErr(noResolverErr);
  159.  
  160.     (void) (*macDNRentry)(ADDRTOSTR, theIPaddr, theString);
  161.  
  162. }
  163.  
  164.  
  165. /*______________________________________________________________________
  166. **
  167. ** DoAddrToName
  168. **
  169. **    Get the canonical name of a host as specified by IP address. Completion is handled by
  170. **    HandleAddrToName method.
  171. **
  172. **        theIPaddr (ip_addr):    the IP address to query
  173. **
  174. */
  175.  
  176.  
  177. void CTCPResolverCall::DoAddrToName (ip_addr theIPaddr)
  178.  
  179. {
  180.     OSErr theResult;
  181.     
  182.     if (inUse)
  183.         FailOSErr(resolverInUse);
  184.     if (!gTCPDriver->CheckResolver())
  185.         FailOSErr(noResolverErr);
  186.     
  187.     inUse = TRUE;
  188.     gTCPDriver->RegisterActiveResolver(this);
  189.     theResult = (*macDNRentry)(ADDRTONAME, theIPaddr, &theHostInfo,
  190.                             &PostponeAddrToName, (char *) this);
  191.  
  192.  
  193.     // if call was completed immediately (with error or not), process it immediately
  194.     
  195.     if (theResult != cacheFault) {
  196.         inUse = FALSE;
  197.         gTCPDriver->RemoveActiveResolver(this);
  198.         HandleAddrToName();
  199.     }
  200. }
  201.  
  202.  
  203. /*______________________________________________________________________
  204. **
  205. ** DoHInfo
  206. **
  207. **    Get the CPU type and operating system type of a remote host. Completion is handled by
  208. **    HandleHInfo.
  209. **
  210. **        theHostName (char *):    who we askin’ ‘bout anyway?
  211. **
  212. */
  213.  
  214. void CTCPResolverCall::DoHInfo (char *theHostName)
  215.  
  216. {
  217.     OSErr theResult;
  218.     
  219.     if (inUse)
  220.         FailOSErr(resolverInUse);
  221.     if (!gTCPDriver->CheckResolver())
  222.         FailOSErr(noResolverErr);
  223.     
  224.     inUse = TRUE;
  225.     gTCPDriver->RegisterActiveResolver(this);
  226.     BlockMove(theHostName, &hostName, 255);
  227.     theResult = (*macDNRentry)(HINFO, (char *) &hostName, &theHMXInfo,
  228.                             &PostponeHInfo, (char *) this);
  229.  
  230.  
  231.     // if call was completed immediately (with error or not), process it immediately
  232.     
  233.     if (theResult != cacheFault) {
  234.         inUse = FALSE;
  235.         gTCPDriver->RemoveActiveResolver(this);
  236.         HandleHInfo();
  237.     }
  238. }
  239.  
  240.  
  241. /*______________________________________________________________________
  242. **
  243. ** DoMXInfo
  244. **
  245. **    Get the mailbox exchange (MX) info of a remote host. Completion is handled by
  246. **    HandleMXInfo.
  247. **
  248. **        theHostName (char *):    who we askin’ ‘bout anyway?
  249. **
  250. */
  251.  
  252. void CTCPResolverCall::DoMXInfo (char *theHostName)
  253.  
  254. {
  255.     OSErr theResult;
  256.     
  257.     if (inUse)
  258.         FailOSErr(resolverInUse);
  259.     if (!gTCPDriver->CheckResolver())
  260.         FailOSErr(noResolverErr);
  261.     
  262.     inUse = TRUE;
  263.     gTCPDriver->RegisterActiveResolver(this);
  264.     BlockMove(theHostName, &hostName, 255);
  265.     theResult = (*macDNRentry)(MXINFO, (char *) &hostName, &theHMXInfo,
  266.                             &PostponeMXInfo, (char *) this);
  267.  
  268.  
  269.     // if call was completed immediately (with error or not), process it immediately
  270.     
  271.     if (theResult != cacheFault) {
  272.         inUse = FALSE;
  273.         gTCPDriver->RemoveActiveResolver(this);
  274.         HandleMXInfo();
  275.     }
  276. }
  277.  
  278.  
  279. //    —— respond to completion of resolver calls ——
  280.  
  281. /*______________________________________________________________________
  282. **
  283. ** ProcessNotify
  284. **
  285. **    Handles any notifications passed to the resolver. This routine is free of interrupt-level
  286. **    constraints.
  287. **
  288. */
  289.  
  290. void CTCPResolverCall::ProcessNotify (void)
  291.  
  292. {
  293.     // free this resolver object for future use
  294.     
  295.     gTCPDriver->RemoveActiveResolver(this);
  296.     inUse = FALSE;    
  297.     
  298.  
  299.     // dispatch notification to appropriate routine
  300.     
  301.     switch (pendingNotify) {
  302.         case notifStrToAddr:
  303.             HandleStrToAddr();
  304.             break;
  305.             
  306.         case notifAddrToName:
  307.             HandleAddrToName();
  308.             break;
  309.             
  310.         case notifHInfo:
  311.             HandleHInfo();
  312.             break;
  313.             
  314.         case notifMXInfo:
  315.             HandleMXInfo();
  316.     }
  317.     
  318.     
  319.     // if someone attempted to dispose this earlier, do it now
  320.     
  321.     if (disposeOnCompletion)
  322.         Dispose();
  323.     
  324. }
  325.  
  326.  
  327. /*______________________________________________________________________
  328. **
  329. ** HandleStrToAddr (protected method)
  330. **
  331. **    Respond to completion of a StrToAddr call.
  332. **
  333. */
  334.  
  335.  
  336. void CTCPResolverCall::HandleStrToAddr (void)
  337.  
  338. {
  339.     BroadcastChange(tcpResolverStrToAddr, &theHostInfo);
  340. }
  341.  
  342.  
  343. /*______________________________________________________________________
  344. **
  345. ** HandleAddrToName (protected method)
  346. **
  347. **    Respond to completion of an AddrToName call.
  348. **
  349. */
  350.  
  351. void CTCPResolverCall::HandleAddrToName (void)
  352.  
  353. {
  354.     BroadcastChange(tcpResolverAddrToName, &theHostInfo);
  355. }
  356.  
  357.  
  358. /*______________________________________________________________________
  359. **
  360. ** HandleHInfo (protected method)
  361. **
  362. **    Respond to completion of an HInfo call.
  363. **
  364. */
  365.  
  366.  
  367. void CTCPResolverCall::HandleHInfo (void)
  368.  
  369. {
  370.     BroadcastChange(tcpResolverHInfo, &theHMXInfo);
  371. }
  372.  
  373.  
  374. /*______________________________________________________________________
  375. **
  376. ** HandleMXInfo (protected method)
  377. **
  378. **    Respond to completion of an MXInfo call.
  379. **
  380. */
  381.  
  382.  
  383. void CTCPResolverCall::HandleMXInfo (void)
  384.  
  385. {
  386.     BroadcastChange(tcpResolverMXInfo, &theHMXInfo);
  387. }
  388.  
  389.  
  390. //    —— open/close TCP resolver ——
  391.  
  392. /*______________________________________________________________________
  393. **
  394. ** OpenResolver (static method)
  395. **
  396. **    Find the MacTCP DNR code resource and read it. Save the location of the resolver resource
  397. **    for later use.
  398. **
  399. */
  400.  
  401. void CTCPResolverCall::OpenResolver (void)
  402.  
  403. {
  404.     short    vRefNum;
  405.     short    refnum;
  406.     long        dirID;
  407.     short    fRef;
  408.     OSErr    rc;
  409.     
  410.     
  411.     // is the resolver already there?
  412.     
  413.     if (macDNRentry)
  414.         return;
  415.  
  416.  
  417.     // open the MacTCP driver to get DNR resources
  418.  
  419.     TRY {
  420.         refnum = OpenTheDNR();
  421.     }
  422.     CATCH {
  423.         NO_PROPAGATE;                    // ignore failures since the resource may
  424.                                         // have been installed in the System file
  425.                                         // (if running on a Mac 512Ke)
  426.     }
  427.     ENDTRY;
  428.  
  429.  
  430.     // load the DNR resource package
  431.     
  432.     macDNRcode = GetIndResource('dnrp', 1);
  433.     FailNIL(macDNRcode);
  434.     DetachResource(macDNRcode);
  435.  
  436.     if (refnum != -1)
  437.         CloseResFile(refnum);
  438.  
  439.         
  440.     // lock the DNR resource since it cannot be relocated while opened
  441.  
  442.     MoveHHi(macDNRcode);
  443.     HLock(macDNRcode);
  444.     macDNRentry = (DNRProcPtr) *macDNRcode;
  445.  
  446.  
  447.     // check for “hosts” file in System Folder (BRB)
  448.  
  449.     GetSystemFolder(&vRefNum, &dirID);
  450.     rc = HOpen(vRefNum, dirID, "\pHosts", fsRdPerm, &fRef);
  451.     switch (rc) {
  452.         case noErr:
  453.             FSClose(fRef);
  454.             break;
  455.             
  456.         case fnfErr:
  457.             if ((rc = HCreate(vRefNum, dirID, "\pHosts", 'ttxt', 'TEXT')) != noErr)
  458.                 ErrorAlert(rc, SpecifyMsg(1001, 1));
  459.             break;
  460.             
  461.         default:
  462.             break;
  463.     }
  464.  
  465.  
  466.     // ask the DNR resource to open the resolver
  467.  
  468.     rc = (*macDNRentry)(OPENRESOLVER, NULL);        // send it a null hosts file name
  469.     if (rc != noErr) {
  470.         HUnlock(macDNRcode);                    // problem with open resolver, flush DNR resource
  471.         DisposHandle(macDNRcode);
  472.         macDNRcode = NULL;
  473.         macDNRentry = NULL;
  474.         FailOSErr(rc);                            // signal failure of DNR
  475.     }
  476.  
  477. }
  478.  
  479.  
  480. /*______________________________________________________________________
  481. **
  482. ** CloseResolver (static method)
  483. **
  484. **    Shut down the DNR code resource. Does nothing if the resolver was never loaded.
  485. **
  486. */
  487.  
  488. void CTCPResolverCall::CloseResolver (void)
  489.  
  490. {
  491.  
  492.     // ensure that we had a resolver to begin with
  493.     
  494.     if (macDNRentry == NULL)
  495.         return;
  496.  
  497.  
  498.     // call CloseResolver function in DNR
  499.  
  500.     (void) (*macDNRentry)(CLOSERESOLVER);
  501.  
  502.  
  503.     // release the DNR code resource
  504.  
  505.     HUnlock(macDNRcode);
  506.     DisposHandle(macDNRcode);
  507.     macDNRcode = NULL;
  508.     macDNRentry = NULL;
  509.  
  510. }
  511.  
  512.  
  513. /*______________________________________________________________________
  514. **
  515. ** OpenTheDNR (protected static method)
  516. **
  517. **    Search in several places for the MacTCP DNR code resource. Tries the Control Panels
  518. **    folder and the System folder.
  519. **
  520. **        return (short):    reference number to resource file
  521. **
  522. */
  523.  
  524. short CTCPResolverCall::OpenTheDNR (void)
  525.  
  526. {
  527.     short    refnum;
  528.     short    vRefNum;
  529.     long        dirID;
  530.     
  531.     
  532.     // first search Control Panels for MacTCP 1.1.x
  533.  
  534.     GetCPanelFolder(&vRefNum, &dirID);
  535.     refnum = SearchFolderForDNRP('cdev', 'ztcp', vRefNum, dirID);
  536.     if (refnum != -1)
  537.         return (refnum);
  538.     
  539.     
  540.     // next search System Folder for MacTCP 1.0.x
  541.  
  542.     GetSystemFolder(&vRefNum, &dirID);
  543.     refnum = SearchFolderForDNRP('cdev', 'mtcp', vRefNum, dirID);
  544.     if (refnum != -1)
  545.         return (refnum);
  546.  
  547.  
  548.     // finally, search Control Panels for MacTCP 1.0.x
  549.  
  550.     GetCPanelFolder(&vRefNum, &dirID);
  551.     refnum = SearchFolderForDNRP('cdev', 'mtcp', vRefNum, dirID);
  552.     if (refnum != -1)
  553.         return (refnum);
  554.     
  555.     return (-1);
  556.  
  557. }
  558.  
  559.  
  560. /*______________________________________________________________________
  561. **
  562. ** SearchFolderForDNRP (protected static method)
  563. **
  564. **    Search a folder for files that might contain the 'dnrp' resource.
  565. **
  566. **        targetType (OSType):    file type that we are looking for
  567. **        targetCreator (OSType):    file creator    ""        ""
  568. **        vRefNum (short):        volume ref num
  569. **        dirID (long):            directory number on the volume
  570. **
  571. **        return (short):            the refnum of the file if found, -1 if not found
  572. **
  573. */
  574.  
  575. short CTCPResolverCall::SearchFolderForDNRP (long targetType, long targetCreator,
  576.                                     short vRefNum, long dirID)
  577.  
  578. {
  579.     HParamBlockRec    fi;
  580.     Str255            filename;
  581.     short            refnum;
  582.     
  583.     
  584.     // initialize our search mechanism
  585.     
  586.     fi.fileParam.ioCompletion = nil;
  587.     fi.fileParam.ioNamePtr = filename;
  588.     fi.fileParam.ioVRefNum = vRefNum;
  589.     fi.fileParam.ioDirID = dirID;
  590.     fi.fileParam.ioFDirIndex = 1;
  591.     
  592.     
  593.     // keep looking till we run out of files
  594.     
  595.     while (PBHGetFInfo(&fi, false) == noErr) {
  596.     
  597.         // scan the folder for files that match our type & creator
  598.  
  599.         if (fi.fileParam.ioFlFndrInfo.fdType == targetType &&
  600.             fi.fileParam.ioFlFndrInfo.fdCreator == targetCreator) {
  601.  
  602.             // type/creator match, look for the resource
  603.             
  604.             refnum = HOpenResFile(vRefNum, dirID, filename, fsRdPerm);
  605.             if (GetIndResource('dnrp', 1) == NULL)
  606.                 CloseResFile(refnum);
  607.             else
  608.                 return (refnum);
  609.         }
  610.  
  611.         // no match or no resource, try next file in folder
  612.  
  613.         fi.fileParam.ioFDirIndex++;
  614.         fi.fileParam.ioDirID = dirID;                // PBHGetFInfo() clobbers ioDirID
  615.     }
  616.     
  617.     return (-1);                                // nothing found
  618.  
  619. }    
  620.  
  621.  
  622. /*______________________________________________________________________
  623. **
  624. ** GetSystemFolder (protected static method)
  625. **
  626. **    Return the ID of the current system folder.
  627. **
  628. **        vRefNumP (short *):        returns volume ref number
  629. **        dirIDP (long *):            returns directory ID
  630. **
  631. */
  632.  
  633. void CTCPResolverCall::GetSystemFolder (short *vRefNumP, long *dirIDP)
  634.  
  635. {
  636.     SysEnvRec    info;
  637.     long            wdProcID;
  638.     
  639.     SysEnvirons(1, &info);
  640.     if (GetWDInfo(info.sysVRefNum, vRefNumP, dirIDP, &wdProcID) != noErr) {
  641.         *vRefNumP = 0;
  642.         *dirIDP = 0;
  643.     }
  644. }
  645.  
  646.  
  647. /*______________________________________________________________________
  648. **
  649. ** GetCPanelFolder (protected static method)
  650. **
  651. **    Return the ID of the current “Control Panels” folder.
  652. **
  653. **        vRefNumP (short *):        returns volume ref number
  654. **        dirIDP (long *):            returns directory ID
  655. **
  656. */
  657.  
  658. void CTCPResolverCall::GetCPanelFolder (short *vRefNumP, long *dirIDP)
  659.  
  660. {
  661.     Boolean    hasFolderMgr = false;
  662.     long        feature;
  663.     
  664.     
  665.     // see if we have the Folder Manager available
  666.     
  667.     if (TrapAvailable((short) _Gestalt))
  668.         if (Gestalt(gestaltFindFolderAttr, &feature) == noErr)
  669.             hasFolderMgr = TRUE;
  670.  
  671.  
  672.     // if folder manager, use it; else return system folder
  673.         
  674.     if (!hasFolderMgr) {
  675.         GetSystemFolder(vRefNumP, dirIDP);
  676.         return;
  677.     }
  678.     else {
  679.         if (FindFolder(kOnSystemDisk, kControlPanelFolderType,
  680.                     kDontCreateFolder, vRefNumP, dirIDP) != noErr) {
  681.             *vRefNumP = 0;
  682.             *dirIDP = 0;
  683.         }
  684.     }
  685.  
  686. }
  687.  
  688.  
  689. /***********************************************************************
  690. ************************************************************************
  691. **
  692. **    INTERRUPT-LEVEL routines follow. These routines cannot make memory allocations, cannot make
  693. **    synchronous TCP calls, and cannot use the Think C profiler.
  694. **
  695. */
  696.  
  697. #pragma options(!profile)
  698.  
  699.  
  700. //     —— interrupt-level methods: delay processing for non-interrupt status ——
  701.  
  702. /*______________________________________________________________________
  703. **
  704. ** PostponeNotify
  705. **
  706. **    Respond to notification that a DNR call was completed. Delays processing of notification
  707. **    until the next net-event call. This method merely logs the kind of notification and adds this
  708. **    resolver to the list of resolvers to be processed next time through the event loop.
  709. **
  710. **        theNotifType (NotifType):    which resolver call was completed
  711. **
  712. */
  713.  
  714. void CTCPResolverCall::PostponeNotify (NotifType theNotifType)
  715.  
  716. {
  717.     pendingNotify = theNotifType;
  718.     Enqueue((QElemPtr) &qEntry, &(gTCPDriver->asyncQueue));
  719. }
  720.  
  721.  
  722. /*______________________________________________________________________
  723. **
  724. ** PostponeStrToAddr (protected static method)
  725. **
  726. **    Respond to notification that the StrToAddr call was completed.
  727. **
  728. **        hostInfoPtr (…):        pointer to the DNR data structure (ignored)
  729. **        userDataPtr (char *):    user data pointer (assumed to be CTCPResolver *)
  730. **
  731. */
  732.  
  733. pascal void CTCPResolverCall::PostponeStrToAddr (struct hostInfo *hostInfoPtr, char *userDataPtr)
  734.  
  735. {
  736.     if (userDataPtr)
  737.         ((CTCPResolverCall *) userDataPtr)->PostponeNotify(notifStrToAddr);
  738. }
  739.  
  740.  
  741. /*______________________________________________________________________
  742. **
  743. ** PostponeAddrToName (protected static method)
  744. **
  745. **    Respond to notification that the AddrToName call was completed.
  746. **
  747. **        hostInfoPtr (…):        pointer to the DNR data structure (ignored)
  748. **        userDataPtr (char *):    user data pointer (assumed to be CTCPResolver *)
  749. **
  750. */
  751.  
  752. pascal void CTCPResolverCall::PostponeAddrToName (struct hostInfo *hostInfoPtr, char *userDataPtr)
  753.  
  754. {
  755.     if (userDataPtr)
  756.         ((CTCPResolverCall *) userDataPtr)->PostponeNotify(notifAddrToName);
  757. }
  758.  
  759.  
  760. /*______________________________________________________________________
  761. **
  762. ** PostponeHInfo (protected static method)
  763. **
  764. **    Respond to notification that the HInfo call was completed.
  765. **
  766. **        hostInfoPtr (…):        pointer to the DNR data structure (ignored)
  767. **        userDataPtr (char *):    user data pointer (assumed to be CTCPResolver *)
  768. **
  769. */
  770.  
  771. pascal void CTCPResolverCall::PostponeHInfo (struct returnRec *returnRecPtr, char *userDataPtr)
  772.  
  773. {
  774.     if (userDataPtr)
  775.         ((CTCPResolverCall *) userDataPtr)->PostponeNotify(notifHInfo);
  776. }
  777.  
  778.  
  779. /*______________________________________________________________________
  780. **
  781. ** PostponeMXInfo (protected static method)
  782. **
  783. **    Respond to notification that the MXInfo call was completed.
  784. **
  785. **        hostInfoPtr (…):        pointer to the DNR data structure (ignored)
  786. **        userDataPtr (char *):    user data pointer (assumed to be CTCPResolver *)
  787. **
  788. */
  789.  
  790. pascal void CTCPResolverCall::PostponeMXInfo (struct returnRec *returnRecPtr, char *userDataPtr)
  791.  
  792. {
  793.     if (userDataPtr)
  794.         ((CTCPResolverCall *) userDataPtr)->PostponeNotify(notifMXInfo);
  795. }
  796.  
  797.